package com.csun.cmny.provider.device.card;

import com.csun.cmny.base.enums.ErrorCodeEnum;
import com.csun.cmny.provider.controller.websocket.WebSocketUtils;
import com.csun.cmny.provider.device.card.entity.CardCmdEntity;
import com.csun.cmny.provider.device.card.entity.CardConstant;
import com.csun.cmny.provider.device.card.entity.CardDataEntity;
import com.csun.cmny.provider.device.phone.entity.PhoneConstant;
import com.csun.cmny.provider.device.phone.entity.PhoneDataEntity;
import com.csun.cmny.provider.exceptions.DmcBizException;
import com.csun.cmny.provider.model.domain.DmcRfidEvent;
import com.csun.cmny.provider.model.domain.DmcRfidEventOldman;
import com.csun.cmny.provider.model.vo.card.ControllerCardVo;
import com.csun.cmny.provider.model.vo.card.RfidReaderVo;
import com.csun.cmny.provider.model.vo.rfid.DmcRfidLabelVo;
import com.csun.cmny.provider.service.DmcControllerCardBindService;
import com.csun.cmny.provider.service.DmcRfidEventOldmanService;
import com.csun.cmny.provider.service.DmcRfidEventService;
import com.csun.cmny.provider.service.DmcRfidLabelOldmanService;
import com.csun.cmny.util.DateUtil;
import com.csun.cmny.util.PublicUtil;
import com.csun.cmny.util.RedisKeyUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Hex;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.util.Date;

/**
 * @Auther: chisj chisj@foxmal.com
 * @Date: 2019-11-28 20:43
 * @Description:
 */
@Slf4j
@Component
public class CardMessageHandler {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    @Resource
    private DmcRfidLabelOldmanService dmcRfidLabelOldmanService;
    @Resource
    private DmcRfidEventService dmcRfidEventService;
    @Resource
    private DmcControllerCardBindService dmcControllerCardBindService;
    @Resource
    private WebSocketUtils webSocketUtils;
    @Resource
    private DmcRfidEventOldmanService dmcRfidEventOldmanService;

    // 暂时都是固定值
    byte[] password = ByteBuffer.allocate(4).order(ByteOrder.BIG_ENDIAN).putInt(0xffffffff).array();
    byte[] code = ByteBuffer.allocate(4).order(ByteOrder.BIG_ENDIAN).putInt(0xffffffff).array();

    // 长度 0x0d
    byte[] bh_length = {0x00, 0x00, 0x00, 0x0d};
    // 长度 0x17
    byte[] mc_length = {0x00, 0x00, 0x00, 0x11};

    /**
     * 处理门禁控制卡消息
     *
     * @param ctx   the channel handler context
     * @param cardCmdEntity  the card cmd entity
     */
    public void handler(ChannelHandlerContext ctx, CardCmdEntity cardCmdEntity) {

        password = cardCmdEntity.getPassword();

        String deviceNo = new String(cardCmdEntity.getSn());
        log.info("==> 门禁控制卡 deviceNo = {}", deviceNo);

        // TODO 暂不缓存门禁控制卡数据
//        String redisKey = RedisKeyUtil.getControllerCardKey(deviceNo);
//        CardDataEntity cardDataEntity = (CardDataEntity) redisTemplate.opsForValue().get(redisKey);
//        if (PublicUtil.isEmpty(cardDataEntity)) {
//            log.error("==> 门禁控制卡 尚未绑定 deviceNo = {}", deviceNo);
//        }

        switch (cardCmdEntity.getType()) {

            case 0x19 :
                /**
                 * 7E FF FF FF FF 4D 43 2D 35 39 32 34 54 32 30 30 38 34 38 38 31 FF FF FF FF 19 23 00 00 00 00 00 B8 7E
                 */
                if (cardCmdEntity.getCmd() == 0x23) {
                    log.info("==> 门禁控制卡 初次连接 deviceNo = {}", deviceNo);
                    CardConstant.CARD_CHANNEL_MAP.put(deviceNo, ctx.channel());
                    // ctx.channel().attr(CardConstant.CARD_CHANNEL_KEY).set(cardDataEntity);
                }
                /**
                 * 7E FF FF FF FF 4D 43 2D 35 39 32 34 54 32 30 30 38 34 38 38 31 FF FF FF FF 19 22 00 00 00 00 00 B7 7E
                 */
                if (cardCmdEntity.getCmd() == 0x22) {
                    log.info("==>  门禁控制卡 再次连接 deviceNo = {}", deviceNo);
                    CardConstant.CARD_CHANNEL_MAP.put(deviceNo, ctx.channel());
                }

                if (cardCmdEntity.getCmd() == 0x01) {
                    log.info("==>  门禁控制卡 刷卡记录 deviceNo = {}", deviceNo);
                    /**
                     * 刷卡指令
                     * 7E FF FF FF FF 43 55 2D 42 48 38 30 31 31 37 30 37 30 36 31 31
                     * FF FF FF FF 19 01 00 00 00 00 0D 00 00 F7 4F 9B 20 09 14 11 21 52 01 0C 4D 7E
                     */
                    /**
                     * 读卡记录格式
                     * 00 00 F7 4F 9B 20 09 14 11 21 52 01 0C
                     *
                     * ------------------------------------------------------
                     *  卡号(5b)  |   时间(6b)  |   读卡器(1b) |   状态(1b)  |
                     *  ------------------------------------------------------
                     *
                     *  读卡器号：1、2表示门1；
                     *           3、4表示门2；
                     *           5、6表示门3；
                     *           7、8表示门4
                     *
                     *  读卡器状态：
                     *   12(0x0c)   --  非法卡
                     *
                     */
                    // 判断控制板类型 -- BH板子
                    int length = byteArrToInt(cardCmdEntity.getLength());
                    byte[] card;
                    byte label = 0x01, status;
                    String cardNo = "";
                    if (13 == length) {
                        card = new byte[5];
                        // 卡号
                        System.arraycopy(cardCmdEntity.getData(), 0, card, 0, 5);
                        // 读卡器
                        label = cardCmdEntity.getData()[11];
                        // 状态
                        status = cardCmdEntity.getData()[12];
                        cardNo = bytesToHexString(card);
                        log.info("==>  门禁控制卡 刷卡记录 BH板子, card no = [{}], label = [{}], status = [{}]",
                                cardNo, label, status);
                        // 补充前4个字节
                        cardNo = "00000000" + cardNo;

                    }
                    /**
                     * 7E FF FF FF FF 4D 43 2D 35 39 32 34 54 32 30 30 38 34 38 38 31
                     * FF FF FF FF 19 01 00 00 00 00 11 00 00 00 00 00 00 F7 4F 9B 20 09 14 14 53 18 01 0C 51 7E
                     */
                    /**
                     * 读卡记录格式
                     * 00 00 00 00 00 00 F7 4F 9B 20 09 14 14 53 18 01 0C
                     *
                     * ------------------------------------------------------
                     *  卡号(9b)  |   时间(6b)  |   读卡器(1b) |   状态(1b)  |
                     *  ------------------------------------------------------
                     *
                     *  读卡器号：1、2表示门1；
                     *           3、4表示门2；
                     *           5、6表示门3；
                     *           7、8表示门4
                     *
                     *  读卡器状态：
                     *   12(0x0c)   --  非法卡
                     *
                     */
                    // 判断控制板类型 -- MC板子
                    if (17 == length) {
                        card = new byte[9];
                        // 卡号
                        System.arraycopy(cardCmdEntity.getData(), 0, card, 0, 9);
                        // 读卡器
                        label = cardCmdEntity.getData()[15];
                        // 状态
                        status = cardCmdEntity.getData()[16];
                        cardNo = bytesToHexString(card);
                        log.info("==>  门禁控制卡 刷卡记录 MC板子, card no = [{}], label = [{}], status = [{}]",
                                cardNo, label, status);
                    }

                    // 开门流程
                    cardOpenDoor(deviceNo, cardNo, label);
                }

                break;

            default :
                log.error("未识别的消息 tpye = {}, cmd = {}",
                        CardMessageDecoder.byteTo16(cardCmdEntity.getType()), cardCmdEntity.getCmd());
        }

    }

    /**
     * 处理刷卡开门
     *
     * @param deviceNo  门禁控制器编号
     * @param cardNo    门禁卡编号
     * @param label     门禁读卡器编号 -- 位置
     */
    public void cardOpenDoor(String deviceNo, String cardNo, byte label) {

        String message = ""; // String.format("%s非法操作", DateUtil.dateToString(new Date()));
        DmcRfidEventOldman dmcRfidEventOldman = new DmcRfidEventOldman();

        ControllerCardVo controllerCardVo = dmcControllerCardBindService.queryControllerCardByDeviceNo(deviceNo);
        if (PublicUtil.isEmpty(controllerCardVo)) {
            log.error("====> chisj 门禁控制器不存在");
            return;
        }

        if (PublicUtil.isEmpty(controllerCardVo.getRfidReaderVoList())) {
            log.error("====> chisj 门禁控制器未绑定");
            return;
        }
        dmcRfidEventOldman.setGroupId(controllerCardVo.getGroupId());

        // 根据门禁卡编号查询门禁卡信息
        boolean flag = true;
        DmcRfidLabelVo dmcRfidLabelVo = dmcRfidLabelOldmanService.selectByRfidLabelNo(cardNo);
        if (dmcRfidLabelVo == null) { // 门禁门禁卡是否存在
            log.error("====> chisj 门禁卡不存在");
            flag = false;
            message = String.format("%s在%s非法操作", cardNo, DateUtil.dateToString(new Date()));
        }

        if (dmcRfidLabelVo.getRoleCode() == null) {
            log.error("====> chisj 门禁卡尚未绑定");
            flag = false;
            message = String.format("%s在%s非法操作", cardNo, DateUtil.dateToString(new Date()));
        }


        if (flag) {
            for (RfidReaderVo rfidReaderVo : controllerCardVo.getRfidReaderVoList()) {

                if (rfidReaderVo.getRfidReaderNo().endsWith(byteToHexString(label))) {

                    // 保存刷卡记录
                    DmcRfidEvent dmcRfidEvent = new DmcRfidEvent();
                    dmcRfidEvent.setOldmanId(dmcRfidLabelVo.getOldmanId());
                    dmcRfidEvent.setOldmanName(dmcRfidLabelVo.getOldmanName());
                    dmcRfidEvent.setRoleId(dmcRfidLabelVo.getRoleId());
                    dmcRfidEvent.setRoleCode(dmcRfidLabelVo.getRoleCode());
                    dmcRfidEvent.setRoleName(dmcRfidLabelVo.getRoleName());
                    dmcRfidEvent.setLocation(rfidReaderVo.getLocation());
                    dmcRfidEvent.setGroupId(dmcRfidLabelVo.getGroupId());
                    dmcRfidEvent.setLabelId(dmcRfidLabelVo.getId());
                    dmcRfidEvent.setLabelNo(dmcRfidLabelVo.getDeviceNo());
                    dmcRfidEvent.setReaderId(rfidReaderVo.getRfidReaderId());
                    dmcRfidEvent.setReaderNo(rfidReaderVo.getRfidReaderNo());
                    dmcRfidEventService.save(dmcRfidEvent);


                    // 门禁老人记录
                    dmcRfidEventOldman.setId(dmcRfidEvent.getId());
                    dmcRfidEventOldman.setGroupId(dmcRfidLabelVo.getGroupId());
                    dmcRfidEventOldman.setLocation(rfidReaderVo.getLocation());
                    dmcRfidEventOldman.setNumber(rfidReaderVo.getNumber());
                    if (PublicUtil.isNotEmpty(dmcRfidLabelVo.getOldmanId())) {
                        dmcRfidEventOldman.setOldmanId(dmcRfidLabelVo.getOldmanId().intValue());
                    }
                    dmcRfidEventOldman.setOldmanName(dmcRfidLabelVo.getOldmanName());
                    message = String.format("%s在%s经过%s",
                            dmcRfidLabelVo.getOldmanName(), DateUtil.dateToString(new Date()), rfidReaderVo.getLocation());
                    dmcRfidEventOldman.setMessage(message);
                    dmcRfidEventOldmanService.save(dmcRfidEventOldman);

                    // 开门
                    if (label == 0x01) {
                        byte[] doors = { 0x01, 0x00, 0x00, 0x00};   // 开门一
                        openDoor(deviceNo, doors);
                    } else {
                        byte[] doors = { 0x00, 0x01, 0x00, 0x00};   // 开门二
                        openDoor(deviceNo, doors);
                    }


                    break;
                }
            }
        }

        if (PublicUtil.isEmpty(message)) {
            message = message = String.format("%s在%s经过未定义门禁",
                    dmcRfidLabelVo.getOldmanName(), DateUtil.dateToString(new Date()));
        }
        dmcRfidEventOldman.setMessage(message);

        // 推送刷卡记录
        webSocketUtils.pushWebsocketMessageWithCard(dmcRfidEventOldman);
    }

    /**
     * 开门指令
     */
    public void openDoor(String deviceNo, byte[] doors) {

        byte[] cmd = CardMessageEncoder.buildOpenDoorCmd(deviceNo, password, code, doors).getBytes();

        sendCmd(deviceNo, cmd);
    }

    /**
     * 关门指令
     */
    public void closeDoor(String deviceNo, byte[] doors) {

        byte[] cmd = CardMessageEncoder.buildCloseDoorCmd(deviceNo, password, code, doors).getBytes();

        sendCmd(deviceNo, cmd);
    }

    /**
     * 发送数据
     *
     * @param deviceNo
     * @param cmd
     */
    public void sendCmd(String deviceNo, byte[] cmd) {

        Channel channel = CardConstant.CARD_CHANNEL_MAP.get(deviceNo);

        if (channel != null) {
            if (channel.isActive()) {
                log.info("门禁控制卡待下发命令: cmd = {}", CardMessageEncoder.format(Hex.encodeHexString(cmd)));
                channel.writeAndFlush(Unpooled.copiedBuffer(cmd));
            } else {
                log.warn("门禁控制卡[{}]连接断开...", deviceNo);
            }
        } else {
            log.warn("门禁控制卡[{}]不在线...", deviceNo);
        }

    }

    public static int byteArrToInt(byte[] arr){
//        System.out.print("需要转换的字节数组是:");
//        for(byte temp : arr){
//            System.out.print(temp+" ");
//        }
//        System.out.println("");
        int x = ((arr[0] & 0xff) << 24 )|((arr[1]& 0xff) <<16 )|((arr[2] & 0xff)<<8)|(arr[3] & 0xff);
//        System.out.println("转换后的整数是:"+x);
        return x;
    }

    public static String bytesToHexString(byte[] src){
        StringBuilder stringBuilder = new StringBuilder("");
        if (src == null || src.length <= 0) {
            return null;
        }
        for (int i = 0; i < src.length; i++) {
            int v = src[i] & 0xFF;
            String hv = Integer.toHexString(v);
            if (hv.length() < 2) {
                stringBuilder.append(0);
            }
            stringBuilder.append(hv);
        }
        return stringBuilder.toString();
    }

    public static String byteToHexString(byte src) {

        StringBuilder stringBuilder = new StringBuilder("");

        int v = src & 0xFF;
        String hv = Integer.toHexString(v);
        if (hv.length() < 2) {
            stringBuilder.append(0);
        }
        stringBuilder.append(hv);

        return stringBuilder.toString();
    }
}
